# Import required modules
from __future__ import annotations
import os
import sys
import json
from datetime import datetime
from typing import List, Dict, Any, Optional, Tuple

# Add project root directory to system path
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(current_dir, ".."))
sys.path.append(project_root)

# Import local classes
from Assignment.Assignment import Assignment, AssignmentSubmission, Assignable

# Assignment management system for handling creation, query, submission and grading
class AssignmentSystem:    
    def __init__(self, data_path: str, course_system, user_system):
        # Initialize assignment management system
        self.data_path = data_path
        self.course_system = course_system  # Course system instance
        self.user_system = user_system      # User system instance
        self.assignments: Dict[str, Assignment] = {}  # Assignment ID to object mapping
        self._load_assignments()
    
    def _parse_deadline(self, deadline_str: str) -> datetime:
        # Parse deadline string with multiple format support
        for fmt in ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d']:
            try:
                if fmt == '%Y-%m-%d':
                    return datetime.strptime(deadline_str, fmt).replace(hour=23, minute=59)
                return datetime.strptime(deadline_str, fmt)
            except ValueError:
                continue
        raise ValueError(f"Invalid date format: {deadline_str}")
    
    def _validate_course_object(self, course) -> bool:
        # Validate course object contains required attributes
        if not course:
            return False
        required_attrs = ['ID', 'CourseName']
        missing_attrs = [attr for attr in required_attrs if not hasattr(course, attr)]
        if missing_attrs:
            print(f"Course object missing required attributes: {missing_attrs}")
            return False
        return True
    
    def _load_assignments(self) -> None:
        # Load assignment data from JSON file
        try:
            if os.path.exists(self.data_path):
                with open(self.data_path, 'r', encoding='utf-8') as f:
                    assignment_data_list = json.load(f)
                
                for assignment_data in assignment_data_list:
                    required_fields = ['id', 'course_id', 'title', 'deadline']
                    missing_fields = [field for field in required_fields if field not in assignment_data]
                    if missing_fields:
                        print(f"Assignment {assignment_data.get('id', 'unknown ID')} missing fields: {missing_fields}, skipping")
                        continue
                    
                    per_course = self.course_system.get_course_by_id(assignment_data['course_id'])
                    if not self._validate_course_object(per_course):
                        print(f"Warning: Invalid course object for ID {assignment_data['course_id']}, skipping assignment {assignment_data['id']}")
                        continue
                    
                    try:
                        deadline = self._parse_deadline(assignment_data['deadline'])
                    except ValueError as e:
                        print(f"Failed to parse deadline: {e}, skipping assignment {assignment_data['id']}")
                        continue
                    
                    description = assignment_data.get('description') or assignment_data.get('content', '')
                    if not description:
                        print(f"Warning: Assignment {assignment_data['id']} missing description, using default")
                        description = "No description available"
                    
                    assignment = Assignment(
                        ID=assignment_data['id'],
                        Title=assignment_data['title'],
                        Description=description,
                        Deadline=deadline,
                        course=per_course
                    )
                    
                    for submission_data in assignment_data.get('submissions', []):
                        student = self.user_system.get_user_by_id(submission_data['student_id'])
                        if student:
                            submission = assignment.submit(student, submission_data['content'])
                            submission.grade = submission_data.get('grade')
                            submission.feedback = submission_data.get('feedback', "")
                            submission.graded_by = submission_data.get('graded_by')
                    
                    self.assignments[assignment.ID] = assignment
                
                print(f"Successfully loaded {len(self.assignments)} assignments")
            else:
                print("Assignment data file not found, initialized empty system")
                
        except json.JSONDecodeError:
            print("Invalid JSON format in assignment data file, initialized empty system")
            self.assignments = {}
        except Exception as e:
            print(f"Error loading assignment data: {str(e)}")
            self.assignments = {}
    
    def _save_assignments(self) -> None:
        # Save assignment data to JSON file
        try:
            assignment_data_list = [assignment.to_dict() for assignment in self.assignments.values()]
            with open(self.data_path, 'w', encoding='utf-8') as f:
                json.dump(assignment_data_list, f, ensure_ascii=False, indent=2)
            print("Assignment data saved successfully")
        except Exception as e:
            print(f"Failed to save assignment data: {str(e)}")
    
    def create_assignment(self, title: str, description: str, deadline: datetime, course_id: str) -> Tuple[bool, str]:
        # Create new assignment with auto-generated unique ID
        assignment_id = self._generate_unique_assignment_id(course_id)
        
        if assignment_id in self.assignments:
            return False, f"Assignment ID {assignment_id} already exists, please try again"
        
        per_course = self.course_system.get_course_by_id(course_id)
        if not self._validate_course_object(per_course):
            return False, f"Invalid course object for ID {course_id}"
        
        try:
            assignment = Assignment(
                ID=assignment_id,
                Title=title,
                Description=description,
                Deadline=deadline,
                course=per_course
            )
            
            self.assignments[assignment.ID] = assignment
            self._save_assignments()
            return True, f"Assignment '{title}' created successfully, ID: {assignment_id}"
        except Exception as e:
            import traceback
            print(f"Assignment creation error stack: {traceback.format_exc()}")
            return False, f"Failed to create assignment: {str(e)}"
    
    def _generate_unique_assignment_id(self, course_id: str) -> str:
        # Generate course-specific unique assignment ID (format: courseID_Asequence)
        course_assignments = self.get_assignments_by_course(course_id)
        
        max_sequence = 0
        for assignment in course_assignments:
            if assignment.ID.startswith(f"{course_id}_A") and assignment.ID[len(course_id)+2:].isdigit():
                sequence = int(assignment.ID[len(course_id)+2:])
                if sequence > max_sequence:
                    max_sequence = sequence
        
        new_sequence = max_sequence + 1
        return f"{course_id}_A{new_sequence}"
    
    def get_assignment_by_id(self, assignment_id: str) -> Optional[Assignment]:
        # Get assignment by ID
        return self.assignments.get(assignment_id)
    
    def get_assignments_by_course(self, course_id: str) -> List[Assignment]:
        # Get all assignments for a specific course
        valid_assignments = []
        for assignment in self.assignments.values():
            try:
                if assignment.Course.ID == course_id:
                    valid_assignments.append(assignment)
            except AttributeError as e:
                print(f"Assignment {assignment.ID} has invalid course attribute: {str(e)}, skipped")
        return valid_assignments
    
    def submit_assignment(self, assignment_id: str, student_id: str, content: str) -> Tuple[bool, str]:
        # Submit assignment
        assignment = self.get_assignment_by_id(assignment_id)
        if not assignment:
            return False, f"Assignment ID {assignment_id} does not exist"
        
        student = self.user_system.get_user_by_id(student_id)
        if not student:
            return False, f"Student ID {student_id} does not exist"
        
        if assignment.is_submitted_by(student):
            return False, "This student has already submitted this assignment"
        
        try:
            assignment.submit(student, content)
            self._save_assignments()
            return True, "Assignment submitted successfully"
        except Exception as e:
            return False, f"Failed to submit assignment: {str(e)}"
    
    def grade_assignment(self, assignment_id: str, student_id: str, grade: float, feedback: str, teacher_id: str) -> bool:
        # Grade assignment - does not handle time information
        try:
            with open(self.data_path, 'r', encoding='utf-8') as f:
                assignments = json.load(f)
            
            assignment_index = None
            for i, assignment in enumerate(assignments):
                if assignment.get('id') == assignment_id:
                    assignment_index = i
                    break
            
            if assignment_index is None:
                print(f"Assignment not found: {assignment_id}")
                return False
            
            submission_found = False
            for submission in assignments[assignment_index].get('submissions', []):
                if submission.get('student_id') == student_id:
                    submission['grade'] = grade
                    submission['feedback'] = feedback
                    submission['graded_by'] = teacher_id
                    
                    for key in ['graded_at', 'submitted_at']:
                        if key in submission:
                            del submission[key]
                    
                    submission_found = True
                    break
            
            if not submission_found:
                print(f"No submission record found for student {student_id}")
                return False
            
            with open(self.data_path, 'w', encoding='utf-8') as f:
                json.dump(assignments, f, ensure_ascii=False, indent=2)
            
            return True
        except Exception as e:
            print(f"Failed to save grade: {str(e)}")
            return False
        
    def get_submissions_by_assignment(self, assignment_id: str) -> List[Any]: 
        """Get all submission records by assignment ID - New auxiliary method""" 
        try: 
            with open(self.data_path, 'r', encoding='utf-8') as f: 
                assignment_data_list = json.load(f) 
                for assignment_data in assignment_data_list: 
                    if assignment_data.get('id') == assignment_id: 
                        return assignment_data.get('submissions', []) 
            return [] 
        except Exception as e: 
            print(f"Failed to get submission records: {str(e)}") 
            return []